package org.sampleSpringFramework.core;

import com.gill.controller.aop.Controller1;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sampleSpringFramework.aop.annotation.Aspect;
import org.sampleSpringFramework.core.annotation.Component;
import org.sampleSpringFramework.core.annotation.Controller;
import org.sampleSpringFramework.core.annotation.Repository;
import org.sampleSpringFramework.core.annotation.Service;
import org.sampleSpringFramework.util.ClassUtil;
import org.sampleSpringFramework.util.ValidationUtil;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

// spring的bean容器
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)//私有无参构造函数
public class BeanContainer {

	/**
	 * 1、参照枚举方式，实现线程安全的、能够防止反射、序列化的单例容器
	 */
	public static BeanContainer getInstance(){
		return ContainerHolder.HOLDER.instance;
	}

	private enum ContainerHolder{
		HOLDER;
		BeanContainer instance;
		private ContainerHolder(){
			instance = new BeanContainer();}
	}

	/**
	 * 2、存放所有被配置标记的目标对象的Map
	 */
	private final Map<Class<?>,Object> beanMap = new ConcurrentHashMap<>();

	/**
	 * 3、存放注解类信息的List，作用（扫描标注了以下注解的类）
	 */
	private static final List<Class<? extends Annotation>> BEAN_ANNOTATION =
			Arrays.asList(Component.class, Controller.class, Service.class, Repository.class, Aspect.class);//AOP功能：扫描@Aspect注解标记的类

	/**
	 * 4、容器是否已经加载过bean
	 */
	private boolean loaded = false;

	/**
	 * 5、是否已加载过Bean
	 *
	 * @return 是否已加载
	 */
	public boolean isLoaded() {
		return loaded;
	}

	/**
	 * 6、Bean实例数量
	 *
	 * @return 数量
	 */
	public int size() {
		return beanMap.size();
	}

	/**
	 * 扫描加载所有Bean
	 * @param packageName
	 */
	public synchronized void loadBeans(String packageName){
		if(isLoaded()){
			log.warn("BeanContainer has been loaded.");
			return;
		}
		Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
		if(ValidationUtil.isEmpty(classSet)){
			log.warn("extract nothing from packageName" + packageName);
			return;
		}
		for (Class<?> clazz: classSet){
			for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
				// 判断类上是否添加了我们自定义的注解
				if(clazz.isAnnotationPresent(annotation)){
					//将目标类本身作为键，目标类的实例作为值，放入到beanMap中
					beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
				}
			}
		}
		loaded = true;
	}

	/**
	 *添加一个class对象及其Bean实例
	 * @param clazz class对象
	 * @param bean bean实例
	 * @return 原有的Bean实例, 没有则返回null
	 */
	public Object addBean(Class<?> clazz,Object bean){
		return beanMap.put(clazz,bean);
	}

	/**
	 *移除一个IOC容器管理的对象
	 * @param clazz Class对象
	 * @return 删除的Bean实例, 没有则返回null
	 */
	public Object removeBean(Class<?> clazz){
		return beanMap.remove(clazz);
	}

	/**
	 * 根据Class对象获取Bean实例
	 * @param clazz Class对象
	 * @return Bean实例
	 */
	public Object getBean(Class<?> clazz){
		return beanMap.get(clazz);
	}

	/**
	 * 获取容器管理的所有Class对象集合
	 * @return Class集合
	 */
	public Set<Class<?>> getClasses(){
		return beanMap.keySet();
	}

	/**
	 * 获取所有Bean集合
	 * @return Bean集合
	 */
	public Set<Object> getBeans(){
		return new HashSet<>( beanMap.values());
	}

	/**
	 * 筛选标记了指定注解的Bean的Class集合
	 * @param annotation 注解
	 * @return Class集合
	 */
	public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
		//1.获取beanMap的所有class对象
		Set<Class<?>> keySet = getClasses();
		if(ValidationUtil.isEmpty(keySet)){
			log.warn("nothing in beanMap");
			return null;
		}
		//2.通过注解筛选被注解标记的class对象，并添加到classSet里
		Set<Class<?>> classSet = new HashSet<>();
		for(Class<?> clazz : keySet){
			//类是否有相关的注解标记
			if(clazz.isAnnotationPresent(annotation)){
				classSet.add(clazz);
			}
		}
		return classSet.size() > 0? classSet: null;
	}

	/**
	 * 通过接口或者父类获取实现类或者子类的Class集合，不包括其本身
	 *
	 * @param interfaceOrClass 接口Class或者父类Class
	 * @return Class集合
	 */
	public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
		//1.获取beanMap的所有class对象
		Set<Class<?>> keySet = getClasses();
		if(ValidationUtil.isEmpty(keySet)){
			log.warn("nothing in beanMap");
			return null;
		}
		//2.判断keySet里的元素是否是传入的接口或者类的子类，如果是，就将其添加到classSet里
		Set<Class<?>> classSet = new HashSet<>();
		for(Class<?> clazz : keySet){
			//判断keySet里的元素是否是传入的接口或者类的子类
			if(interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)){
				classSet.add(clazz);
			}
		}
		return classSet.size() > 0? classSet: null;
	}

}
